package com.leenmvc.core.init;

import com.leenmvc.core.mqtt.annotation.TopicMapping;
import com.leenmvc.core.config.MqttConfig;
import com.leenmvc.core.mqtt.entity.TopicMappingInfo;
import com.leenmvc.core.mqtt.service.PushCallbackService;
import com.leenmvc.core.utils.StringUtils;
import com.leenmvc.core.utils.dynamic.ClassLoaderUtil;
import com.leenmvc.core.utils.spring.Spring;
import com.leenmvc.core.utils.spring.SpringContextUtils;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

public class InitMqtt {

    private final static Logger log = LoggerFactory.getLogger(InitMqtt.class);

    public static void initMqtt() {
        log.info("初始化Mqtt");
        PushCallbackService.topicMapping = getMqttMap("TopicController");
        ConfigurableListableBeanFactory beanFactory = Spring.APP.getBeanFactory();
        MqttClient mqttClient = null;
        try {
            MqttConfig mqttConfig = SpringContextUtils.getBean(MqttConfig.class);
            log.info("正在连接服务器为：" + mqttConfig.getBroker() + mqttConfig.getPort() + "的emq消息中间件...");
            //UUID作为客户端Id
            String clientId = UUID.randomUUID().toString();
            mqttClient = new MqttClient(mqttConfig.getBroker() + ":" + mqttConfig.getPort(), clientId, new MemoryPersistence());
            //设置qos安全等级
            int[] Qos = new int[]{Integer.parseInt(mqttConfig.getQos())};
            MqttConnectOptions mqttConnectOptions = getMqttConnectOptions(mqttConfig);
            // 声明mqtt回调类
            PushCallbackService pushCallbackService = SpringContextUtils.getBean(PushCallbackService.class);
            pushCallbackService.setMqttClient(mqttClient);
            pushCallbackService.setMqttConnectOptions(mqttConnectOptions);
            pushCallbackService.setQos(Qos);
            pushCallbackService.setTopics(new String[]{mqttConfig.getTopic()});
            //设置mqtt客户端回调信息
            mqttClient.setCallback(pushCallbackService);
            //设置mqtt客户端连接属性 并连接
            mqttClient.connect(mqttConnectOptions);
            //订阅主题
            //订阅
            mqttClient.subscribe(new String[]{mqttConfig.getTopic()}, Qos);
            beanFactory.registerSingleton("mqttClient", mqttClient);
            log.info("客户端创建成功");
        } catch (MqttException e) {
            log.error(e.getMessage(), e);
        }
    }

    public static MqttConnectOptions getMqttConnectOptions(MqttConfig mqttConfig) {
        // MQTT的连接设置
        MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
        //掉线的时候会清空客户端中的session ， 等待重连的时候创建了一个新的session
        mqttConnectOptions.setCleanSession(true);
        //用户名密码认证时候用到
        mqttConnectOptions.setUserName(mqttConfig.getUserName());
        mqttConnectOptions.setPassword(mqttConfig.getPassword().toCharArray());
        mqttConnectOptions.setConnectionTimeout(2000);
        // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线，但这个方法并没有重连的机制
        mqttConnectOptions.setKeepAliveInterval(20);
        //设置断开后重新连接
        mqttConnectOptions.setAutomaticReconnect(true);
        return mqttConnectOptions;
    }

    public static Map<String, TopicMappingInfo> getMqttMap(String endWith) {
        log.info("mqttinfoMap初始化");
        Map<String, TopicMappingInfo> mqttMap = new HashMap<>();
        List<Class<?>> allClassNames = new ClassLoaderUtil().getAllClassNames(endWith);
        log.info("size："+allClassNames.size());
        for (Class<?> cls : allClassNames) {
            TopicMapping annotation1 = cls.getAnnotation(TopicMapping.class);
            String classPath = annotation1.value();
            Method[] declaredMethods = cls.getDeclaredMethods();
            for (Method method : declaredMethods) {
                TopicMappingInfo topicMappingInfo = new TopicMappingInfo();
                TopicMapping annotation = method.getAnnotation(TopicMapping.class);
                if (annotation == null) continue;
                String methodPath = annotation.value();
                Object controllerBean = SpringContextUtils.getBean(StringUtils.lowercaseFirstChar(cls.getSimpleName()));
                topicMappingInfo.setMethod(method);
                topicMappingInfo.setObj(controllerBean);
                mqttMap.put(classPath + methodPath, topicMappingInfo);
            }
        }
        for (String key :mqttMap.keySet()) {
            log.info("topic:" + mqttMap.get(key));
        }
        return mqttMap;
    }
}
